#include "md5.h" // 包含 MD5 的头文件，定义了相关常量和函数
#include <iomanip> // 用于格式化输出
#include <assert.h> // 用于断言
#include <chrono> // 用于时间测量

using namespace std;
using namespace chrono;
/**
 * StringProcess: 将单个输入字符串转换成 MD5 计算所需的消息数组
 * @param input 输入字符串
 * @param[out] n_byte 用于返回填充后的消息长度
 * @return 填充后的消息数组
 */
Byte *StringProcess(string input, int *n_byte)
{
    // 将输入字符串转换为 Byte 数组
    Byte *blocks = (Byte *)input.c_str();
    int length = input.length(); // 获取输入字符串的长度

    // 计算原始消息长度（以比特为单位）
    int bitLength = length * 8;

    // 计算需要的填充位数，使消息长度满足 MD5 的要求
    int paddingBits = bitLength % 512;
    if (paddingBits > 448)
    {
        paddingBits += 512 - (paddingBits - 448); // 如果超过 448 位，则需要额外填充到下一个块
    }
    else if (paddingBits < 448)
    {
        paddingBits = 448 - paddingBits; // 如果不足 448 位，则填充到 448 位
    }
    else if (paddingBits == 448)
    {
        paddingBits = 512; // 如果正好是 448 位，则需要额外填充一个块
    }

    // 计算需要的填充字节数
    int paddingBytes = paddingBits / 8;

    // 计算最终消息的总长度（原始长度 + 填充字节 + 8 字节的长度信息）
    int paddedLength = length + paddingBytes + 8;
    Byte *paddedMessage = new Byte[paddedLength]; // 分配内存用于存储填充后的消息

    // 复制原始消息到填充后的消息数组
    memcpy(paddedMessage, blocks, length);

    // 添加填充字节，第一个填充字节为 0x80，后续填充字节为 0
    paddedMessage[length] = 0x80;                             // 添加 0x80
    memset(paddedMessage + length + 1, 0, paddingBytes - 1);  // 填充 0

    // 添加消息长度（64 位，小端格式）
    for (int i = 0; i < 8; ++i)
    {
        paddedMessage[length + paddingBytes + i] = ((uint64_t)length * 8 >> (i * 8)) & 0xFF;
    }

    // 验证填充后的长度是否是 512 位的倍数
    int residual = 8 * paddedLength % 512;
    // assert(residual == 0); // 如果不满足，程序会终止

    // 返回填充后的消息长度
    *n_byte = paddedLength;
    return paddedMessage; // 返回填充后的消息数组
}
/**
 * MD5Hash: 将单个输入字符串转换成 MD5 哈希值
 * @param input 输入字符串
 * @param[out] state 用于存储最终的 MD5 哈希值
 */
void MD5Hash(string input, bit32 *state)//1.传入的参数也要改
{
    Byte *paddedMessage; // 用于存储填充后的消息
    int *messageLength = new int[1]; // 用于存储填充后的消息长度

    // 调用 StringProcess 函数对输入字符串进行填充
    paddedMessage = StringProcess(input, &messageLength[0]);    //2.改成填充四个

    // 计算消息块的数量，每块 64 字节（512 位）
    int n_blocks = messageLength[0] / 64;

    // 初始化 MD5 的 4 个状态变量
    state[0] = 0x67452301; // A
    state[1] = 0xefcdab89; // B
    state[2] = 0x98badcfe; // C
    state[3] = 0x10325476; // D    //3.改成向量状态

    // 逐块处理填充后的消息
    for (int i = 0; i < n_blocks; i += 1)//4.改成处理填充后的向量
    {
        bit32 x[16]; // 用于存储当前块的 16 个 32 位整数

        // 将当前块的 512 位数据分解为 16 个 32 位整数
        for (int i1 = 0; i1 < 16; ++i1)
        {
            x[i1] = (paddedMessage[4 * i1 + i * 64]) |
                    (paddedMessage[4 * i1 + 1 + i * 64] << 8) |
                    (paddedMessage[4 * i1 + 2 + i * 64] << 16) |
                    (paddedMessage[4 * i1 + 3 + i * 64] << 24);
        }

        // 初始化本块的状态变量
        bit32 a = state[0], b = state[1], c = state[2], d = state[3];//5.改成初始化并行状态变量

        // Round 1 - FF 操作
        FF(a, b, c, d, x[0], s11, 0xd76aa478);
        FF(d, a, b, c, x[1], s12, 0xe8c7b756);
        // ... 省略其他 FF 操作 ...

        // Round 2 - GG 操作
        GG(a, b, c, d, x[1], s21, 0xf61e2562);
        GG(d, a, b, c, x[6], s22, 0xc040b340);
        // ... 省略其他 GG 操作 ...

        // Round 3 - HH 操作
        HH(a, b, c, d, x[5], s31, 0xfffa3942);
        HH(d, a, b, c, x[8], s32, 0x8771f681);
        // ... 省略其他 HH 操作 ...

        // Round 4 - II 操作
        II(a, b, c, d, x[0], s41, 0xf4292244);
        II(d, a, b, c, x[7], s42, 0x432aff97);//6.改成并行II操作
        // ... 省略其他 II 操作 ...

        // 更新全局状态
        state[0] += a;
        state[1] += b;
        state[2] += c;
        state[3] += d;//7.改
    }

    // 调整字节序（小端转大端）
    for (int i = 0; i < 4; i++)
    {
        uint32_t value = state[i];
        state[i] = ((value & 0xff) << 24) |        // 将最低字节移到最高位
                   ((value & 0xff00) << 8) |       // 将次低字节左移
                   ((value & 0xff0000) >> 8) |     // 将次高字节右移
                   ((value & 0xff000000) >> 24);   // 将最高字节移到最低位
    }//8.调整字节序的方法可能也要改

    // 释放动态分配的内存
    delete[] paddedMessage;
    delete[] messageLength;
}
//MD5 的核心思想是将输入数据分块处理，通过一系列复杂的数学运算，将数据“压缩”成一个固定长度的哈希值。

// 1. 填充数据
// 为了让数据长度满足 MD5 的要求（长度是 512 位的倍数），需要对数据进行填充。
// 填充规则：
// 在数据末尾添加一个 1（即 0x80）。
// 然后添加若干个 0，直到数据长度满足 长度 % 512 = 448。
// 最后再附加 64 位，表示原始数据的长度（以比特为单位）。
// 2. 分块处理
// 将填充后的数据分成若干个 512 位的块，每个块再分成 16 个 32 位的小块。